﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Linq.Expressions;
using TaskManager.Models.Interfaces.Repository;
using TaskManager.Models.Entities;
using System.Monads;
using TaskManager.Models.Context;


namespace TaskManager.Models.Repository
{
    //Репозиторий - класс который принимает объекты по шаблону (BaseEntity)
    //и напрямую содержит низкоуровневые функции для работы с базой данный.
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : BaseEntity
    {

        public DataContext Context = new DataContext();

        /// <summary>
        /// Get entities from repository
        /// </summary>
        /// <param name="filter">Filter</param>
        /// <param name="orderBy">Order by property</param>
        /// <param name="includeProperties"></param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {


            var query = Context.Set<TEntity>().AsQueryable();

            var test = query.ToList();

            // Apply filter
            filter.Do((s) =>
            {
                query = query.Where(filter);
            });


            // Apply includes
            foreach (var includeProperty in includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            // Apply order by if need

            var result = orderBy
                 .With(x => x(query))
                 .Return(x => x, query);


            return result;
        }


        /// <summary>
        /// Find entity by key
        /// </summary>
        /// <param name="id">Identificator</param>
        /// <returns>Entity</returns>
        public virtual TEntity GetById(object id)
        {
            //throw new NotImplementedException();
            return Context.Set<TEntity>().Find(id);
        }

        /// <summary>
        /// Add new entity to repository
        /// </summary>
        /// <param name="entity">New entity</param>
        public virtual void Insert(TEntity entity)
        {
            AttachIsNotAttached(entity);
            Context.Set<TEntity>().Add(entity);
            Context.SaveChanges();
        }

        /// <summary>
        /// Updated entity in repositoy
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void Update(TEntity entity)
        {
            AttachIsNotAttached(entity);
            Context.Entry(entity).State = EntityState.Modified;
            Context.SaveChanges();
        }

        /// <summary>
        /// Remove entity from rpository
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void Delete(TEntity entity)
        {
            AttachIsNotAttached(entity);
            Context.Set<TEntity>().Remove(entity);
            Context.Entry(entity).State = EntityState.Deleted;
            Context.SaveChanges();
        }

        /// <summary>
        /// Attach entity 
        /// </summary>
        /// <param name="entity"></param>
        protected void AttachIsNotAttached(TEntity entity)
        {
            if (Context.Entry(entity).State == EntityState.Detached)
            {
                var attached = Context.Set<TEntity>().Local.Where(x => x.Id == entity.Id).FirstOrDefault();

                attached.Do((s) =>
                {
                    Context.Entry(s).State = EntityState.Detached;
                });

                Context.Set<TEntity>().Attach(entity);
            }
        }
    }
}
